#! /usr/bin/env python
"""
TODO:

I intend (possibly) to write other segments that report (but not log):
1.  items (name and/or stats, if that's even possible)
2.  reputation (more viable, and somewhat practical)

Also I want to clean up the code to make it more modular.

Also I want to (but don't really anticipate that I will) set it up so if a character wasn't updated on the wowarmory server it won't process the character's data again.
"""

import urllib2
import xml.dom.minidom
import sys
import logging
import os

logpath = os.path.normpath('/home/derek/.wowarm') + '/'     # needs to end in /
armoryserver = 'http://www.wowarmory.com/'                  # needs to end in /
checkmelee = checkranged = checkspell = checktank = True    # it's possible to change these at the command line but I don't feel like implementing that atm
#checkmelee = False     # in case you want to disable data tracking -- shouldn't need to disable these normally
#checkranged = False
#checkspell = False
#checktank = False

class wowarm:

    def init(self, core, logstate): #load up the character's basics
        # immutable attributes
        self.charclass = core.getAttribute('class')
        charrace = core.getAttribute('race')
        chargender = core.getAttribute('gender')
        charfaction = core.getAttribute('faction')
        charrealm = core.getAttribute('realm')
        charbg = core.getAttribute('battleGroup')

        logform = [
            '%s',               # self.titledname
            '%s (%s) - %s',     # charrealm, charbg, charfaction
            '%s - %s - %s\n',   # self.charclass, charrace, chargender
            ]

        if logstate == 'fresh':
            logging.info(logform[0], self.titledname)
            logging.info(logform[1], charrealm, charbg, charfaction)
            logging.info(logform[2], self.charclass, charrace, chargender)
        else:
            print
            print logform[0] % (self.titledname)
            print logform[1] % (charrealm, charbg, charfaction)
            print logform[2] % (self.charclass, charrace, chargender)
            print

        # mutable attributes
        lastmod = core.getAttribute('lastModified')
        self.charlevel = int(core.getAttribute('level'))
        guildname = core.getAttribute('guildName') and '<' + core.getAttribute('guildName') + '>'
        logging.info('='*35)
        logging.info('Updated: %s', lastmod)
        logging.info('%s - Level %d\n', guildname or '/guildless/', self.charlevel)
        if self.titles: logging.info('Known titles: %s\n', self.titles)

    def update(self):
        logging.info('%20s  %s', 'spec', self.spec)
        logging.info('%20s  %d', 'honor kills', self.kills)
        for prof in self.professions:
            logging.info('%20s  %s', prof.getAttribute('name'), prof.getAttribute('value'))
        for buff in self.buffs:
            logging.info('BUFF: %s -- %s', buff.getAttribute('name'), buff.getAttribute('effect'))
        for debuff in self.debuffs:
            logging.info('DEBUFF: %s -- %s', debuff.getAttribute('name'), buff.getAttribute('effect'))
        logging.info('%20s  %5d', 'health', self.health)
        logging.info('%20s  %5d', 'mana', self.mana)
        logging.info('%20s  %5d\n', 'mp5', self.mp5)

    def fetch(self, server, character):
        get_url = armoryserver + "character-sheet.xml?r=%s&n=%s" % (server, character)
        oOpener = urllib2.build_opener()
        oOpener.addheaders = [('user-agent', 'Firefox/2.0 - wowarmpy 0.3'),]
        print 'Making request to server.',
        req = urllib2.Request(get_url)
        strFile = oOpener.open(req).read()
        print 'Received response.'
        oDoc = xml.dom.minidom.parseString(strFile)
        return oDoc

    def get_stats(self, charclass, melee, ranged, caster, tank):
        # classId = 6, 10 don't seem to be used

        if charclass == (6 or 10): logging.warning('==========CLASSID %d', charclassid)

        meleeclasses = [
            '1',  #warrior
            '2',  #paladin
            '4',  #rogue
            '7',  #shaman
            '11', #druid
            ]

        rangedclasses = [
            '3',  #hunter
            ]

        casterclasses = [
            '2', ['holy'],                       #paladin
            '5', ['holy', 'shadow'],             #priest
            '7', ['fire', 'frost', 'nature'],    #shaman
            '8', ['arcane', 'fire', 'frost'],    #mage
            '9', ['fire', 'shadow'],             #warlock
            '11', ['arcane', 'nature'],          #druid
            ]

        healerclasses = [
            '2',  #paladin
            '5',  #priest
            '7',  #shaman
            '11', #druid
            ]

        tankclasses = [
            '1',  #warrior
            '2',  #paladin
            '4',  #rogue -- mostly for the whole dodge/parry thing
            '11', #druid
            ]

        meleeclass = rangedclass = casterclass = tankclass = ''

        if melee and charclass in meleeclasses:
            mainhand = melee.getElementsByTagName('mainHandDamage')[0]
            offhand = melee.getElementsByTagName('offHandDamage')[0]
            MHdps = float(mainhand.getAttribute('dps'))
            OHdps = float(offhand.getAttribute('dps'))
            MHspeed = float(mainhand.getAttribute('speed'))
            OHspeed = float(offhand.getAttribute('speed'))
            crit = float(melee.getElementsByTagName('critChance')[0].getAttribute('percent'))
            logform = '%20s  %7.1f, speed %4.2f'
            logging.info(logform, 'Main hand', MHdps, MHspeed)
            logging.info(logform, 'Off hand',  OHdps, OHspeed)
            logging.info('%20s  %8.2f %%\n', 'critChance', crit)

        if ranged and charclass in rangedclasses:
            damage = ranged.getElementsByTagName('damage')[0]
            dps = float(damage.getAttribute('dps'))
            speed = float(damage.getAttribute('speed'))
            crit = float(ranged.getElementsByTagName('critChance')[0].getAttribute('percent'))
            logging.info('%20s  %7.1f, %5.2f, %5.2f %%\n', 'ranged', dps, speed, crit)

        if caster and charclass in casterclasses:
            i = casterclasses.index(charclass) + 1
            bonusDamage = caster.getElementsByTagName('bonusDamage')[0]
            critChance = caster.getElementsByTagName('critChance')[0]
            for school in casterclasses[i]:
                bonus = int(bonusDamage.getElementsByTagName(school)[0].getAttribute('value'))
                crit = float(critChance.getElementsByTagName(school)[0].getAttribute('percent'))
                logging.info('%20s  %5d, %5.2f %%', school, bonus, crit)

        if caster and charclass in healerclasses:
            bonusHealing = int(caster.getElementsByTagName('bonusHealing')[0].getAttribute('value'))
            logging.info('%20s  %5d\n', 'bonusHealing', bonusHealing)

        if tank and charclass in tankclasses:
            armor = tank.getElementsByTagName('armor')[0]
            basearmor = int(armor.getAttribute('base'))
            mitig = float(armor.getAttribute('percent'))
            dodge = float(tank.getElementsByTagName('dodge')[0].getAttribute('percent'))
            parry = float(tank.getElementsByTagName('parry')[0].getAttribute('percent'))
            block = float(tank.getElementsByTagName('block')[0].getAttribute('percent'))
            logform = '%20s  %5.2f %%'
            logging.info('%20s  %d (%5.2f %% against lvl %d)', 'armor', basearmor, mitig, self.charlevel)
            logging.info(logform, 'dodge', dodge)
            logging.info(logform, 'parry', parry)
            logging.info(logform + '\n', 'block', block)

    def __init__(self):
        commandLine = sys.argv.pop(0)   # get that out of the way, and it is practical
        try: init_realm, init_char = sys.argv.pop(0), sys.argv.pop(0)   # command line arguments
        except IndexError:
            print 'The first argument must be the realm name, the second must be the character name. (Use + instead of a space on the realm name.)'
            print commandLine, '<realm name> <character name>'
            sys.exit()

        for arg in sys.argv:
            pass

        source = self.fetch(init_realm, init_char)

        try: charcore = source.getElementsByTagName('character')[0]
        except IndexError: print '%s does not exist on %s or the wowarmory is inaccessible.' % (init_char, init_realm); sys.exit()
        try: self.arenateams = charcore.getElementsByTagName('arenaTeams')[0]; self.arena = True
        except IndexError: self.arena = False

        chartab = source.getElementsByTagName('characterTab')[0]
        talents = chartab.getElementsByTagName('talentSpec')[0]
        self.buffs = chartab.getElementsByTagName('buffs')[0].getElementsByTagName('spell')
        self.debuffs = chartab.getElementsByTagName('debuffs')[0].getElementsByTagName('spell')
        self.professions = chartab.getElementsByTagName('professions')[0].getElementsByTagName('skill')
        knowntitles = chartab.getElementsByTagName('knownTitles')[0].getElementsByTagName('title')
        bars = chartab.getElementsByTagName('characterBars')[0]

        #preprocessing
        self.charname = charcore.getAttribute('name')
        self.charfile = logpath + self.charname
        charrealm = charcore.getAttribute('realm')

        try:    #see if the log file is already there
            openlog = open(self.charfile, 'r')
            openlog.close()
            logstate = 'append'
        except IOError: #file doesn't exist yet; see if we can create it:
            try:
                openlog = open(self.charfile, 'w')
                openlog.close()
                logstate = 'fresh'
                print 'First time seeing %s.' % self.charname
            except IOError: # file can't be created because the value of self.logpath doesn't exist as a directory
                print 'You must create the following path according to the variable set in the code. Feel free to change the code as necessary. The variable name is %s.' % 'logpath'
                print logpath
                sys.exit()

        logging.basicConfig(
            level = logging.INFO,
            format = '%(levelname)-8s %(message)s',
            filename = self.charfile,
            filemode = 'a')
        console = logging.StreamHandler() # define a Handler which writes INFO messages or higher to the sys.stderr
        console.setLevel(logging.DEBUG)
        formatter = logging.Formatter('%(levelname)-8s %(message)s') # set a format which is simpler for console use
        console.setFormatter(formatter) # tell the handler to use this format
        logging.getLogger('').addHandler(console) # add the handler to the root logger; don't know what this does, but it's necessary to send it to the console
        print 'Logging to: %s\n' % self.charfile

        if os.name in ['posix', 'mac']:   # if the system is unix or mac, set up a hard link
            if not os.path.exists(logpath + charrealm): os.mkdir(logpath + charrealm)
            try:
                os.link(self.charfile, logpath + charrealm + '/' + self.charname)
            except: pass    # if links can't work, don't worry about it.

        self.spec = '%d / %d / %d' % (int(talents.getAttribute('treeOne')), int(talents.getAttribute('treeTwo')), int(talents.getAttribute('treeThree')))
        self.kills = int(chartab.getElementsByTagName('pvp')[0].getElementsByTagName('lifetimehonorablekills')[0].getAttribute('value'))
        self.titledname = charcore.getAttribute('prefix') + self.charname + charcore.getAttribute('suffix')
        self.titles = ''
        for title in knowntitles:
            self.titles = title.getAttribute('value') % self.charname + (self.titles and '; ' + self.titles)
        self.titles = self.titles
        self.health = int(bars.getElementsByTagName('health')[0].getAttribute('effective'))
        self.mana = int(bars.getElementsByTagName('secondBar')[0].getAttribute('effective'))
        self.mp5 = int(bars.getElementsByTagName('secondBar')[0].getAttribute('casting'))

        self.init(charcore, logstate)
        self.update()

        melee = checkmelee and chartab.getElementsByTagName('melee')[0]
        ranged = checkranged and chartab.getElementsByTagName('ranged')[0]
        spell = chartab.getElementsByTagName('spell')
        caster = checkspell and spell[len(spell) - 1]
        tank = checktank and chartab.getElementsByTagName('defenses')[0]
        charclassid = charcore.getAttribute('classId')

        self.get_stats(charclassid, melee, ranged, caster, tank)

        #pvp()
# basic structure of the xml:
'''
characterInfo
    character
        arenaTeams
            arenaTeam contribution, ranking, rating, size, etc
                members
                    character
    characterTab
        talentSpec
        buffs
            spell effect, name
        debuffs
            spell effect, name
        pvp
            lifetimehonorablekills value
            arenacurrency value
        professions
            skill name, value
        title (current, selected)
        knownTitles
            title value
        characterBars
            health effective
            secondBar casting, effective
        baseStats
            ...
        resistances
            ...
        melee
            mainHandDamage dps, speed
            offHandDamage dps, speed
            power increasedDps
            hitRating increasedHitPercent
            critChance percent
        ranged
            damage dps, speed
            power increasedDps
            hitRating increasedHitPercent
            critChance percent
        spell
            bonusDamage
                ... value
            bonusHealing value
            hitRating increasedHitPercent
            critChance
                ... percent
            penetration value
        defenses
            armor percent
            defense plusDefense, value
            dodge percent
            parry percent
            block percent
        items
            ...
'''



if __name__ == "__main__":
    wowarm()
